<template>
  <div ref="scroll" id="scroll" class="throttle-func">
    <Row class="title-top mb-10">
      <h2>节流与防抖属于性能优化方面,实际上遇到频率极高</h2>
    </Row>
    <Tabs v-model="currentTab" @on-click="tabChange" :animated="false">
      <TabPane label="节流" name="0"></TabPane>
      <TabPane label="防抖" name="1"></TabPane>
    </Tabs>
    <!--节流 -->
    <div v-show="currentTab==='0'">
      <p class="title mb-10">举个栗子:很多网站会提供这么一个按钮：用于返回顶部,即抽象出一个需求：监听浏览器滚动事件，返回当前滚条与顶部的距离</p>
      <p class="mb-10">
        1.document.documentElement 即
        <span class="text">html元素</span>，而 document.body 即
        <span class="text">body元素</span>。
        <br />2.测试结果，在火狐上只能通过html元素来获取和设定页面滚动高度，而在谷歌上反之只能用body元素来获取和设定
        <br />
      </p>
      <p class="mb-10">
        PS:
        <br />1.document.body.scrollTop =>chrom
        <br />2.document.documentElement.scrollTop =>IE 火狐
      </p>
      <p class="title mb-10">
        <span class="title">发现采用 $(document) 可以实现对获取和设定scrollTop的兼容</span>
      </p>
      <div class="mb-10">
        <p class="text">节流案例</p>
        <p>如果短时间内大量触发同一事件，那么在函数执行一次之后，该函数在指定的时间期限内不再工作，直至过了这段时间才重新生效</p>
        <p>
          1、懒加载要监听计算滚动条的位置，使用
          <label class="text">节流</label>按一定时间的频率获取
        </p>
      </div>
      <div class="title mb-10">
        <button id="throttle">点我节流！</button>
      </div>
    </div>

    <!--防抖 -->
    <div v-show="currentTab==='1'">
      <div class="mb-10">
        <span class="text">防抖</span>
        <p>任务频繁触发的情况下，只有任务触发的间隔超过指定间隔的时候，任务才会执行</p>
      </div>
      <div class="title mb-10">
        <span class="text">防抖案例：</span>
        <p>
          1、搜索框input事件，例如要支持输入实时搜索可以使用
          <label class="text">防抖</label>（间隔一段时间就必须查询相关内容），或者实现输入间隔大于某个值（如500ms），就当做用户输入完成，然后开始搜索，具体使用哪种方案要看业务需求。
        </p>
        <p>2、页面resize事件，常见于需要做页面适配的时候。需要根据最终呈现的页面情况进行dom渲染（这种情形一般是使用防抖，因为只需要判断最后一次的变化情况）</p>
      </div>
      <div class="title mb-10">
        <button id="debounce">点我防抖！</button>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { Tools } from "@/model/tools.ts";
import * as $ from "jquery";
@Component
export default class ShakeFunc extends Tools {
  public $refs!: {
    scroll: any;
  };
  private currentTab: string = "0";
  //切换
  private tabChange(val): void {
    this.currentTab = val;
  }
  private showTop() {
    //let el: any = this.$el.children[0];
    // querySelector()方法可以不需要额外的jQuery等支持，也可以方便的获取DOM元素，语法跟jQuery类似
    let element: any = document.querySelector(".app-main");
    console.log("element的高度scrollTop", element.scrollTop);
    let scrollTop = element.scrollTop;
    console.log(`滚动的高度为${scrollTop}px`);
  }

  /**
   * 防抖debounce
   */
  private debounce(fn, deplay) {
    // 借助闭包
    let timer = 0;
    return function() {
      if (timer) {
        clearTimeout(timer);
      }
      timer = setTimeout(fn, deplay);
    };
  }

  /**
   * 节流throttle
   * 也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在，如果存在表示还在冷却，并且在执行fn之后消除定时器表示激活，原理都一样
   */
  private throttle(fn, deplay) {
    let valid = true;
    return function() {
      if (!valid) {
        return; // 休息时间暂不执行任务
      }
      // 工作时间，执行函数并且在间隔期内把状态位设为无效
      valid = false;
      setTimeout(() => {
        fn();
        valid = true;
      }, deplay);
    };
  }

  /**
   * 触发滚动 方法一:使用原生事件监听@scroll.native="",此方法监听不到
   */

  private onScroll(): void {
    this.debounce(this.showTop, 1000); // 为了方便观察效果我们取个大点的间断值，实际使用根据需要来配置
  }

  /**
   * 节流
   */
  private throttleBtn(fn, deplay) {
    // 通过闭包保存一个标记
    let valid = true;
    return function() {
      if (!valid) {
        return;
      }
      valid = false; //将valid设置为false,防止执行之前在执行
      //定时器
      setTimeout(() => {
        fn();
        //执行完事件之后，重新将这个标记设置为true
        valid = true;
      }, deplay);
    };
  }

  private sayThrottle() {
    console.log("节流成功");
  }

  //防抖功能函数，接受传参
  private debounceBtn(fn, deplay) {
    //创建一个标记用来存放定时器的返回值
    let timer = 0;
    return function() {
      //每次用户点击or输入之前，将前一个定时器清除
      if (timer) {
        clearTimeout(timer);
      }
      // 然后创建一个新的setTimeout,保证点击按钮后的interval间隔内，若用户还点击了按钮，则不执行fn函数
      timer = setTimeout(fn, deplay);
    };
  }

  private sayDebounce() {
    //有些防抖的工作，在此处执行
    console.log("防抖成功");
  }

  /**
   * 方法二:使用window.addEventListener(),此方法也监听不到,需要使用this.$nextTick()方法延迟
   */
  mounted() {
    $("#scroll").css("background", "#f8f8f9");
    this.$nextTick(() => {
      // 首先通过$refs获取dom元素 ,需要注入pulic $refs!:{ scroll: any; }
      let scrollDom = this.$refs.scroll;
      //防抖
      //window.addEventListener("scroll", this.debounce(this.showTop, 500), true);
      // 节流
      window.addEventListener("scroll", this.throttle(this.showTop, 500), true);
      //----------------------点击按钮防抖-----------------------------
      if (this.currentTab === "1") {
        var myDebounce: any = document.getElementById("debounce");
        myDebounce.addEventListener(
          "click",
          this.debounceBtn(this.sayDebounce, 1000),
          true
        );
      } else {
        //---------------------点击按钮节流-----------------------------
        var myThrottle: any = document.getElementById("throttle");
        myThrottle.addEventListener(
          "click",
          this.throttleBtn(this.sayThrottle, 1000),
          true
        );
      }
    });
  }

  destroyed() {
    window.removeEventListener(
      "scroll",
      this.throttle(this.showTop, 500),
      true
    );
  }
}
</script>
<style lang="stylus" scoped>
.throttle-func {
  height: 2000px
  .title {
    font-size: 12px
    font-weight: 700
    color: #f19000
  }
  .text {
    font-size: 12px
    font-weight: 700
    color: red
  }
}
</style>
